package main;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;

import obiecte.Background;
import obiecte.SpaceObject;
import obiecte.Sun;

import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.gl2.GLUT;


/**
 * This class serves as mediator between your scene code and 
 * the boilerplate OpenGL code that needs to be written.  It
 * calls your scene methods at the appropriate time.
 * 
 * It may be updated from time to time to add functionality.
 * 
 * @author Robert C. Duvall
 */
public class Listener
    implements GLEventListener, KeyListener
{
    // constants
    public static long ONE_SECOND = 1000;

    // user's scene to animate and display
    private Scene myScene;

    // animation state
    private Animator myAnimator;
    private int myFrameCount;
    private long myLastFrameTime;
    private double myFPS;
    private boolean isRunning;
    private boolean showFPS;
    
    private TextureHandler sun, background, earth, moon;
    private Background bg;
    public ArrayList<TextureHandler> tex ;

    // cache creation of these objects
    private static GLU glu = new GLU();
    private static GLUT glut = new GLUT();


    /**
     * Create this listener with the arguments given on the 
     * command-line and the animation thread.
     * 
     * @param args command-line arguments
     * @param animator animation thread
     */
    public Listener (Scene scene, Animator animator)
    {
        myScene = scene;
        myAnimator = animator;
        isRunning = true;
        myFrameCount = 0;
        myLastFrameTime = System.currentTimeMillis();
        myFPS = 0;
        showFPS = false;
        tex = new ArrayList<TextureHandler>();
    }


    /**
     * Get the title of the scene.
     *
     * @return title of scene
     */
    public String getTitle ()
    {
        return myScene.getTitle();
    }


    ////////////////////////////////////////////////////////////
    // GLEventListener methods
    /**
     * Called once immediately after the OpenGL context is initialized.
     * 
     * @see GLEventListener#init(GLAutoDrawable)
     */
    public void init (GLAutoDrawable drawable)
    {
        // get graphics context
        GL2 gl = (GL2)drawable.getGL();

        // interesting?
        System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
        System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
        System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION));
        System.err.println("GL_CLASS: " + gl.getClass().getName());
        
        
        // Setting the clear color -- the color which will be used to erase the canvas.
 		gl.glClearColor(0, 0, 0, 0);
 		
 		// Selecting the modelview matrix.
 		gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
 		
 		// Create a new GLU object.
 		glu = GLU.createGLU();
 		
 		sun = new TextureHandler(gl, glu, "texturi\\sun.jpg", true);
 		tex.add(sun);
		background = new TextureHandler(gl, glu, "texturi\\mw.jpg", true);   
		earth = new TextureHandler(gl, glu, "texturi\\earth.jpg", true);  
		tex.add(earth);
		moon = new TextureHandler(gl, glu, "texturi\\moon.jpg", true);   
		tex.add(moon);
 		 
    
        gl.glShadeModel(GL2.GL_SMOOTH);
		
		// Activate the depth test and set the depth function.
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glDepthFunc(GL.GL_LESS);
		
		// Set The Texture Generation Mode For S To Sphere Mapping (NEW)
		gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP);                
		// Set The Texture Generation Mode For T To Sphere Mapping (NEW) 
		gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_SPHERE_MAP);
		
		gl.glEnable(GL2.GL_LIGHTING);
		gl.glEnable(GL2.GL_LIGHT0);
		gl.glEnable(GL2.GL_LIGHT1);
		
		gl.glEnable(GL2.GL_COLOR_MATERIAL);
		gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE);
        // start scene        
        myScene.init(gl, glu, glut);
    }


    /**
     * Called repeatedly to render the OpenGL scene.
     * 
     * @see GLEventListener#display(GLAutoDrawable)
     */
    public void display (GLAutoDrawable drawable)
    {
        // get graphics context
        GL2 gl = drawable.getGL().getGL2();

        // update scene for this time step
        myScene.animate(gl, glu, glut);
        // clear the drawing surface
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        // display background
        bg = new Background(background);
		bg.draw(gl);
        // display model
        gl.glPushMatrix();
          	myScene.setCamera(gl, glu, glut);
          	//System.out.println(myScene.getClass());
          	myScene.display(gl, glu, glut, tex);
        gl.glPopMatrix();
        
        // display frame rate
        computeFPS();
    }


    /**
     * Called immediately after the component has been resized
     * 
     * @see GLEventListener#reshape(GLAutoDrawable, int, int, int, int)
     */
    public void reshape (GLAutoDrawable drawable,
                         int x, int y, int width, int height)
    {
        // reset camera based on new viewport
        setPerspective((GL2)drawable.getGL(), glu, GL2.GL_RENDER, null);
    }


    /**
     * Called when the display mode or the display device has changed.
     * 
     * @see GLEventListener#displayChanged(GLAutoDrawable, boolean, boolean)
     */
    public void displayChanged (GLAutoDrawable drawable,
                                boolean modeChanged,
                                boolean deviceChanged)
    {
        // not generally used
    }


    /**
     * Called when the display is closed.
     * 
     * @see GLEventListener#dispose(GLAutoDrawable)
     */
        public void dispose (GLAutoDrawable drawable)
        {
        // not generally used
        }


        ////////////////////////////////////////////////////////////
    // KeyListener methods
    /**
     * Called when any key is pressed within the canvas.
     */
    public void keyPressed (KeyEvent e)
    {
        // pass event onto user's code
        myScene.keyPressed(e.getKeyCode());
    }

    /**
     * Called when any key is released within the canvas.
     */
    public void keyReleased (KeyEvent e)
    {
        switch (e.getKeyCode())
        {
            // toggle animation running
            case KeyEvent.VK_Z:
                showFPS = !showFPS;
                break;
            // toggle animation running
            case KeyEvent.VK_P:
                isRunning = !isRunning;
                if (isRunning) myAnimator.start();
                else           myAnimator.stop();
                break;
            // quit the program
            case KeyEvent.VK_ESCAPE:
            case KeyEvent.VK_Q:
                myAnimator.stop();
                System.exit(0);
                break;
            // pass event onto user's code
            default:
                myScene.keyReleased(e.getKeyCode());
        }
    }

    /**
     * Called when standard alphanumeric keys are pressed and released 
     * within the canvas.
     */
    public void keyTyped (KeyEvent e)
    {
        // by default, do nothing
    }

    
    ////////////////////////////////////////////////////////////
    // helper methods
    /**
     * Reset perspective matrix based on size of viewport.
     */
    private void setPerspective (GL2 gl, GLU glu, int mode, Point pt)
    {
        // get info about viewport (x, y, w, h)
        int[] viewport = new int[4];
        gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);

        // set camera to view viewport area
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        // check for selection
        if (mode == GL2.GL_SELECT)
        {
            // create 5x5 pixel picking region near cursor location
            glu.gluPickMatrix(pt.x, viewport[3] - pt.y, 5.0, 5.0, viewport, 0);
        }
        // view scene in perspective
        glu.gluPerspective(45.0, (float)viewport[2] / (float)viewport[3], 0.1, 500.0);
        // prepare to work with model again
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();
    }


    /*
     * Compute and print frames per second of animation
     */
    private double computeFPS ()
    {
        myFrameCount++;
        long currentTime = System.currentTimeMillis();
        if (currentTime - myLastFrameTime > ONE_SECOND)
        {
            myFPS = myFrameCount * ONE_SECOND / (double)(currentTime - myLastFrameTime);
            myLastFrameTime = currentTime;
            if (showFPS) System.out.printf("%3.2f\n", myFPS);
            myFrameCount = 0;
        }
        
        return myFPS;
    }
}
